package com.clgk.wechatmall.common.util;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;

import static com.clgk.wechatmall.common.util.CoordConvertType.*;

/**
 * google坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 其中 bd_encrypt 将 GCJ-02 坐标转换成 BD-09
 * 坐标， bd_decrypt 反之。
 *
 * @author tcloud
 */

public class CoordConvertorUtils {
    private static String AK = "AEb0131a50350625a882ec3a84f64940";

    private static Logger logger = Logger.getLogger(CoordConvertorUtils.class);

    private static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    /**
     * 将 GCJ-02 坐标转换成 BD-09 坐标(google坐标转百度坐标)
     *
     * @param gg_lat
     * @param gg_lng
     * @return lat, lng
     */
    public static double[] bdEncrypt(double gg_lng, double gg_lat) {
        double x = gg_lng, y = gg_lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double bd_lng = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        return new double[]
                {bd_lng, bd_lat};
    }

    /**
     * 将 BD-09坐标转换成GCJ-02 坐标(百度坐标转google坐标)
     *
     * @param bd_lat
     * @param bd_lng
     * @return lat, lng
     */
    public static double[] bdDecrypt(double bd_lng, double bd_lat) {
        double x = bd_lng - 0.0065, y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gg_lng = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        return new double[]
                {gg_lng, gg_lat};
    }

    /**
     * 百度经纬度转换
     *
     * @param lng  经度
     * @param lat  纬度
     * @param from 源坐标类型
     * @param to   目的坐标类型
     * @return
     */
    public static double[] geoconv(double lng, double lat, int from, int to) {
        String url = "http://api.map.baidu.com/geoconv/v1/?";
        Map<String, String> params = new HashMap<String, String>();
        params.put("from", String.valueOf(from));
        // from 取值为如下：
        //        1：GPS设备获取的角度坐标;
        //
        //        2：GPS获取的米制坐标、sogou地图所用坐标;
        //
        //        3：google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标
        //
        //        4：3中列表地图坐标对应的米制坐标
        //
        //        5：百度地图采用的经纬度坐标
        //
        //        6：百度地图采用的米制坐标
        //
        //        7：mapbar地图坐标;
        //
        //        8：51地图坐标

        //to 有两种可供选择：5、6。
        //        5：bd09ll(百度经纬度坐标),
        //
        //        6：bd09mc(百度米制经纬度坐标);
        params.put("to", String.valueOf(to));
        params.put("coords", lat + "," + lng);
        params.put("output", "json");
        params.put("ak", AK);

        HttpUtils httpUtils = new HttpUtils();
        String response;
        double[] point = new double[2];
        try {
            response = httpUtils.post(url, params);
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode JsonNode = objectMapper.readTree(response);
            JsonNode statusNode = JsonNode.get("status");
            int status = -1;
            if (statusNode != null) {
                status = statusNode.getIntValue();
            }
            if (status == 0) {
                JsonNode locationNode = JsonNode.get("result");
                if (locationNode != null) {
                    point[0] = locationNode.get("x").getDoubleValue();
                    point[1] = locationNode.get("y").getDoubleValue();
                }
            } else {
                point = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return point;
    }

    /**
     * 转换经纬度
     *
     * @param lng  经度
     * @param lat  纬度
     * @param type
     * @return
     */
    public static double[] convert(double lng, double lat, CoordConvertType type) {
        return convert(lng, lat, type, ConvertMode.ONLINE);
    }

    /**
     * 转换经纬度
     *
     * @param lng  经度
     * @param lat  纬度
     * @param type
     * @param convertMode 转换模式：（LOCAL：本地算法转换，速度快，但会有偏差； ONLINE：在线接口转换，精确，但是大批量转换速度慢;目前只适用于转百度经纬度）
     * @return
     */
    public static double[] convert(double lng, double lat, CoordConvertType type, ConvertMode convertMode) {
        double[] point = new double[2];
        try {
            switch (type) {
                case GOOGLE_TO_BAIDU:
                    if (convertMode == ConvertMode.ONLINE) {
                        point = geoconv(lng, lat, 3, 5);
                    } else {
                        point = bdEncrypt(lng, lat);
                    }
                    break;
                case GOOGLE_TO_GPS:
                    double[] pointFixed = MapPointFix
                            .getFixedPoint(lng, lat, false);
                    point[0] = pointFixed[0];
                    point[1] = pointFixed[1];
                    break;
                case BAIDU_TO_GOOGLE:
                    point = bdDecrypt(lng, lat);
                    break;
                case BAIDU_TO_GPS:
                    point = bdDecrypt(lng, lat);
                    double[] pointFixed1 = MapPointFix.getFixedPoint(point[0],
                            point[1], false);
                    point = pointFixed1;
                    break;
                case GPS_TO_GOOGLE:
                    double[] pointFixed2 = MapPointFix
                            .getFixedPoint(lng, lat, true);
                    point = pointFixed2;
                    break;
                case GPS_TO_BAIDU:
                    if (convertMode == ConvertMode.ONLINE) {
                        point = geoconv(lng, lat, 1, 5);
                    } else {
                        double[] pointFixed3 = MapPointFix
                                .getFixedPoint(lng, lat, true);
                        point = bdEncrypt(pointFixed3[0], pointFixed3[1]);
                    }
                    break;
            }

        } catch (Exception e) {
            logger.error("poi convert error", e);
        }

        return point;
    }

    public static void main(String[] args) throws Exception {
        new MapPointFix().init();
        // 百度地图取坐标http://api.map.baidu.com/lbsapi/getpoint/index.html
        // 深圳市宝安区宝民二路111号 港隆城百货
        // 113.887466,22.588602

        System.out.println(ArrayUtils.toString(convert(113.887466, 22.588602,
                BAIDU_TO_GPS)));

        System.out.println(ArrayUtils.toString(convert(113.88101549613705,
                22.5822571919519, GPS_TO_BAIDU)));

        System.out.println(ArrayUtils.toString(convert(116.304461, 39.982941,
                GPS_TO_GOOGLE)));
        // 116.304461,39.982941
    }
}
